Using a Custom Inline Editor


Spread.Views allows you to customize the inline editor to handle complex editing functions. Built-in editors can only edit the primitive data types; however, you can customize the inline editor to edit fields with complicated data types or to customize the editing logic.

In the sample code, the following custom editor types are used.

  • NameEditor - Used for editing short text fields such as first, middle, or last name.
  • RadioButtonEditor - Used for selecting boolean values.
  • TextAreadEditor - Used for editing long text entries.

Use the following steps to customize the inline editor.

Sample Code

These steps assume that you have already initialized the grid and defined the columns. See Creating a Basic Grid and Defining Columns for additional information.

  1. Initialize the code using the Grid ID from the DIV tag and set the allowEditing property to true.

    var dataView = new GC.Spread.Views.DataView(document.getElementById('grid1'), data, columns, new GC.Spread.Views.Plugins.GridLayout({
                allowEditing: true
            }));
    
            function NameEditor(options) {
                this.options = options;
        }
  2. Define the function to specify the NameEditor implementation.

    //Function for Name Editor 
    NameEditor.prototype = {
                init: function() {
                    var options = this.options;
                    var container = options.container;
                    var rawValue = options.value;
                    var rawNames = rawValue.split(' ');
                    var firstName = '';
                    var lastName = '';
                    var middleName = '';
                    var hasMiddleName = false;
                    switch (rawNames.length) {
                        case 0:
                            firstName = '';
                            lastName = '';
                            break;
                        case 1:
                            firstName = rawNames[0];
                            lastName = '';
                            break;
                        case 2:
                            firstName = rawNames[0];
                            lastName = rawNames[1];
                            break;
                        default:
                            firstName = rawNames[0];
                            lastName = rawNames[rawNames.length - 1];
                            middleName = rawNames.slice(1, rawNames.length - 1).join(' ');
                            hasMiddleName = true;
                    }
                    this.defaultValue = rawValue;
                    setAttributes(container, {
                        'styles': {
                            'padding': '3px',
                            'border': '1px solid #0066FF',
                            'background': 'white',
                            'border-radius': '2px'
                        }
                    });
                    container.innerHTML = '<div style="margin-bottom:6px"><div><i>First Name:</i></div><input type="text" class="firstname" style="width:120px" value="' + firstName + '"></div>' +
                        (hasMiddleName ? '<div style="margin-bottom:6px"><div><i>Middle Name:</i></div><input type="text" class="middlename" style="width:120px" value="' + middleName + '"></div>' : '') +
                        '<div><div><i>Last Name:</i></div><input type="text" class="lastname" style="width:120px" value="' + lastName + '"></div>';
                    this.firstNameInput = container.querySelector("input.firstname");
                    this.middleNameInput = container.querySelector("input.middlename");
                    this.lastNameInput = container.querySelector("input.lastname");
                    this.focus();
                },
    
                focus: function() {
                    this.firstNameInput.focus();
                },
    
                serialize: function() {
                    return this.middleNameInput ?
                        this.firstNameInput.value + " " + this.middleNameInput.value + " " + this.lastNameInput.value :
                        this.firstNameInput.value + " " + this.lastNameInput.value;
                }
        };
  3. Define the function to specify the RadioButtonEditor implementation.

    //Function for Radio Editor 
     function RadioButtonEditor(options) {
                var self = this;
                self.options = options;
            }
    
            RadioButtonEditor.prototype = {
                init: function() {
                    var options = this.options;
                    var rawValue = options.value;
                    var container = options.container;
                    setAttributes(container, {
                        'styles': {
                            'min-width': '90px',
                            'padding': '6px',
                            'border': '1px solid #0066FF',
                            'border-radius': '2px'
                        }
                    });
                    var list = ['male', 'female'];
                    var len = list.length;
                    var i = 0;
                    var checked = "";
                    var fragInnerHTML = "";
                    while (i < len) {
                        checked = list[i] !== rawValue ? "" : checked = "checked";
                        fragInnerHTML += '<div><input type="radio" style="outline: 0" name="radioeditor" id="' + list[i] + '" value="' +
                            list[i] + '"' + checked + '><LABEL style="font-weight:normal;padding: 4px;" for="' + list[i] + '">' + list[i] + '</LABEL></div> ';
                        ++i;
                    }
                    container.innerHTML = fragInnerHTML;
                },
    
                focus: function() {
                    this.options.container.querySelector("input:checked").focus();
                },
    
                serialize: function() {
                    return this.options.container.querySelector("input:checked").value;
                }
            };
    
            function TextAreaEditor(options) {
                this.options = options;
        }
  4. Define the function to specify the TextAreaEditor implementation.

    //Function for TextArea Editor 
     TextAreaEditor.prototype = {
                init: function() {
                    var options = this.options;
                    var container = options.container;
                    container.innerHTML = '<textarea style="outline:0;width:100%;height:100%;border:0px;"></textarea>' +
                        '<div style="width:100%">' +
                        '<button class="cancel" style="float:right;width:60px;margin-right: 0.4em; background: #f1f1f1; border: solid 1px #e0e0e0;">Cancel</button>' +
                        '<button class="save" style="float:right;width:60px;margin-right: 0.4em; background: #f1f1f1; border: solid 1px #e0e0e0;">Save</button>' +
                        '</div>';
                    this.textArea = container.children[0];
                    this.cancelButton = container.querySelector("button.textarea-cancel");
                    this.saveButton = container.querySelector("button.textarea-save");
                    setAttributes(container, {
                        'styles': {
                            'padding': '3px',
                            'border': '1px solid #0066FF',
                            'border-radius': '2px'
                        }
                    });
                    this.textArea.value = options.value;
                    this.textArea.focus();
                    this.clickHandler_ = this.containerClickHandler.bind(this);
                    container.addEventListener('keydown', this.containerKeyDownHandler);
                    container.addEventListener('click', this.clickHandler_);
                },
    
                destroy: function() {
                    var container = this.options.container;
                    container.removeEventListener('keydown', this.containerKeyDownHandler);
                    container.removeEventListener('click', this.clickHandler_);
                },
    
                focus: function() {
                    this.textArea.focus();
                },
    
                serialize: function() {
                    return this.textArea.value;
                },
    
                containerKeyDownHandler: function(e) {
                    e.stopPropagation();
                    return false;
                },
    
                containerClickHandler: function(e) {
                    e.stopPropagation();
                    var target = e.target;
                    var options = this.options;
                    var dataview = options.dataview;
                    if (target) {
                        var tagName = target.tagName.toLowerCase();
                        if (tagName === 'button') {
                            var className = target.className;
                            if (className === 'save') {
                                dataview.stopEditing();
                            } else if (className === 'cancel') {
                                dataview.cancelEditing();
                            }
                        }
                    }
                }
            };
    
            function setAttributes(element, attrs) {
                for (var key in attrs) {
                    if (attrs.hasOwnProperty(key)) {
                        var attr = attrs[key];
                        if (key === 'styles') {
                            for (var prop in attr) {
                                if (attr.hasOwnProperty(prop)) {
                                    element.style[prop] = attr[prop];
                                }
                            }
                        } else {
                            element.setAttribute(key, attr);
                        }
                    }
                }
        }